import * as os from "os";
import * as fs from "fs";
import * as cluster from "cluster";
import * as cpuStat from "cpu-stat";
import { EventEmitter } from "events";

import stop from "./stop";
import log from "./lib/log";
import serviceSet from "./serviceSet";
import extensionSet from "./extensionSet";
import { args, argList } from "./lib/args";
import { Config, serviceInfo, OsInfo } from "./typing";
import { AuthController, CheckToken, Auth } from "./lib/auth";

let config: Config = require(args["-c"]);
const undealEvent = new EventEmitter();
@AuthController
export class Operation {
    private workers: Map<cluster.Worker, string>;
    private workersName: Map<string, cluster.Worker>;
    private serviceInfos: { [key: string]: serviceInfo };

    public auth: Auth;
    
    constructor(serviceInfos: { [key: string]: serviceInfo }, workers: Map<cluster.Worker, string>, workersName: Map<string, cluster.Worker>) {
        this.serviceInfos = serviceInfos;
        this.workers = workers;
        this.workersName = workersName;
    }

    public async login(username: string, password: string, token: string): Promise<{ id: string; number: number; lastToken: string; }> {
        return this.auth.login(username, password, token);
    }

    public async logout(token: string) {
        await this.auth.logout(token);
    }

    /**
     * 获取系统当前内存剩余和cpu占用
     */
    public osStatus(): Promise<OsInfo> {
        return new Promise((resolve) => {
            cpuStat.usagePercent((err: NodeJS.ErrnoException, occupy: number) => {
                if (!err) {
                    resolve({
                        freemem: os.freemem(),
                        cpu: occupy,
                        timestamp: new Date().getTime(),
                    });
                }
            });
        });
    }

    /**
     * 启动服务
     * @param serviceName 服务名
     */
    @CheckToken
    public async startService({ serviceName }) {
        if (this.serviceInfos[serviceName].status === "stop" &&!this.workersName.has(serviceName)) {
            this.workers.set(cluster.fork(), serviceName);
        }
    }

    /**
     * 重启服务
     * @param serviceName 服务名
     */
    @CheckToken
    public async restartService(data) {
        const serviceName = data.serviceName;
        if (this.serviceInfos[serviceName].status === "running" && this.workersName.has(serviceName)) {
            this.stopService(data);
        }
        undealEvent.once(serviceName, () => {
            this.workers.set(cluster.fork(), serviceName);
        });
    }

    /**
     * 停止服务
     * @param serviceName 服务名
     */
    @CheckToken
    public async stopService({ serviceName }) {
        if (this.serviceInfos[serviceName].status !== "stop") {
            const workers =  this.workersName.get(serviceName);
            if (workers) {
                workers.process.kill();
            }
        }
    }

    /**
     * 获取服务进程内存占用
     * @param serviceName 服务名
     */
    @CheckToken
    public getServiceMemory({ serviceName }) {
        if (this.workersName[serviceName]) {
            this.workersName[serviceName].send({ type: "getMemory" });
        }
    }

    /**
     * 获取所有服务
     */
    public getServices(): { [key: string]: serviceInfo } {
        // 生成一个新的代理对象解除对原对象的引用，防止数据在插件层被修改
        const servicesProxy = {};
        for (const key in this.serviceInfos) {
            servicesProxy[key] = new Proxy(this.serviceInfos[key], {
                get(target, key, receiver) {
                    return Reflect.get(target, key, receiver);
                },
                set() {
                    return false;
                }
            });
        }

        return new Proxy(servicesProxy, {
            get(target, key, receiver) {
                return Reflect.get(target, key, receiver);
            }
        });
    }

    /**
     * 更新配置文件
     * @param configStr 配置文件内容
     */
    @CheckToken
    public async updateConfig({ configStr }) {
        fs.writeFile(args["-c"], configStr, (error) => {
            if (error) {
                log.app.error("updateConfig", error);
            }
        });
    }

    /**
     * 广播消息到各子进程
     * @param message 消息
     */
    public broadcast(message) {
        for (const worker of this.workers.keys()) {
            worker.send(message);
        }
    }

    /**
     * 调用服务的静态方法
     * @param serviceName 服务名
     * @param funcName 方法名
     * @param args 参数表
     */
    @CheckToken
    public async omnipotent({ serviceName, funcName, args}) {
        try {
            args = args || [];
            const result = await serviceSet[serviceName][funcName](config, ...args);
            return result;
        } catch (error) {
            log.app.error(error);
            return error;
        }
    }

    /**
     * 获取配置文件内容
     */
    public getConfig() {
        return config;
    }
}

export default async function run() {
    // 先停止nginx等服务
    await stop();

    const serviceEvent = new EventEmitter();

    // 设置子进程启动参数
    cluster.setupMaster({ args: argList });

    // 服务进程映射服务名
    const workers = new Map<cluster.Worker, string>();

    // 服务名映射服务进程
    const workersName = new Map<string, cluster.Worker>();

    // 服务状态
    const services: { [key: string]: serviceInfo } = {};

    for (let serviceName in serviceSet) {
        services[serviceName] = { status: "stop" };
    }

    cluster.on("fork", (worker: cluster.Worker) => {
        const name = workers.get(worker) || "";
        workersName.set(name, worker);

        // 通知子进程要执行的任务
        worker.send({ type: name });

        services[name].status = "start";
        services[name].startTime = new Date().getTime();

        serviceEvent.emit("fork", {
            type: "fork",
            serviceName: name,
            startTime: services[name].startTime,
        });
    });

    cluster.on("exit", (worker: cluster.Worker) => {
        const name = workers.get(worker) || "";
        log.app.info(`工作进程 ${ name } 已退出。`);
        workers.delete(worker);
        workersName.delete(name);
        services[name].status = "stop";
        services[name].stopTime = new Date().getTime();

        serviceEvent.emit("exit", {
            type: "exit",
            serviceName: name,
            stopTime: services[name].stopTime,
        });

        undealEvent.emit(name);
    });

    cluster.on("message", function (worker, message) {
        if (!message.type) {
            // try {
            //     const data = JSON.parse(message.data);
            //     const type = data[2];
            //     if (type === "service") {
            //         const time = data[1];
            //         let index = data[3];
            //         console.log(type, time, data[index]);
            //     }
            // } catch (error) {
            //     log.app.error(error);
            // }
            return;
        }
        switch (message.type) {
            case "running":
                services[message.serviceName].status = "running";
                message.runningTime = new Date().getTime();
                services[message.serviceName].runningTime = message.runningTime;
                break;
            case "memory":
                services[message.serviceName].memoryUsage = message.memoryUsage;
                break;
            default: break;
        }
        serviceEvent.emit(message.type, message);
    });

    const operation = new Operation(services, workers, workersName);
    // 插件注入
    for (const extension in extensionSet) {
        new extensionSet[extension](serviceEvent, operation).register();
    }

    // 监听配置文件更新
    fs.watchFile(args["-c"], { interval: 500 }, () => {
        fs.readFile(args["-c"], (err, data) => {
            try {
                config = JSON.parse(data.toString());
                this.configVersion = new Date().getTime()
                operation.broadcast({
                    config,
                    type: "updateConfig",
                });
                serviceEvent.emit("updateConfig");
            } catch (error) {
                log.app.info("watch config:", error);
            }
        });
    });

    // 监听各服务的配置文件
    for (const serviceName in config.service) {
        const service = config.service[serviceName];
        if (service.menuName) {
            serviceSet[serviceName].monitorConfig(config, () => {
                serviceEvent.emit("updateServiceConfig", serviceName);
            });
        }
    }

    // 自启动服务
    if (config.autoStart || config.autoStart.length > 0) {
        log.app.info(`检测到自启动服务: [${ config.autoStart.join(", ") }]`);
        for (const serviceName of config.autoStart) {
            workers.set(cluster.fork(), serviceName);
        }
    }
}
